﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using SZL= ICSharpCode.SharpZipLib.Zip;
using System.IO;

namespace PowerShellZip.Commands
{
    using SZL;

    [Cmdlet(VerbsData.Export,"Zip")]
    public class ExportZip : CmdletBase
    {
        private Dictionary<string, SZL.ZipFile> zipFiles;

        private string[] zipFilePathParam;
        [Parameter(Position = 0,Mandatory=false)]
        [ValidateNotNullOrEmpty]
        public string[] ZipFile
        {
            get { return zipFilePathParam; }
            set { zipFilePathParam = value; }
        }

        private string[] entryFilePathParam;
        [Parameter(Position = 1, Mandatory = false)]
        [ValidateNotNullOrEmpty]
        public string[] EntryFile
        {
            get { return entryFilePathParam; }
            set { entryFilePathParam = value; }
        }

        private string[] entryZipPathParam;
        [Parameter(Position = 2, Mandatory = false)]
        [ValidateNotNullOrEmpty]
        public string[] EntryZip
        {
            get { return entryZipPathParam; }
            set { entryZipPathParam = value; }
        }


        private string[] entryRootPathParam;
        [Parameter(Mandatory=false)]
        [ValidateNotNullOrEmpty]
        public string[] EntryRoot
        {
            get { return entryRootPathParam; }
            set { entryRootPathParam = value; }
        }

        private string[] entryDirectoryParam;
        [Parameter(Mandatory = false)]
        [ValidateNotNullOrEmpty]
        public string[] EntryDir
        {
            get { return entryDirectoryParam; }
            set { entryDirectoryParam = value; }
        }

        [Parameter]
        public SwitchParameter Append { get; set; }

        private PSObject[] pipelineObjectParam = null;
        [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = false)]
        [ValidateNotNullOrEmpty]
        public PSObject[] PipelineObject
        {
            get { return pipelineObjectParam; }
            set { pipelineObjectParam = value; }
        }

        protected override void BeginProcessing()
        {
            zipFiles = new Dictionary<string, SZL.ZipFile>();
        }

        protected override void ProcessRecord()
        {
            string zipFilePath = null; // Path to the Zip File 
            string entryFilePath = null; // Path to the Entry in Filesystem
            string entryZipPath = null; // Path to the Entry in Zip File (Zip File Naming Conventions)
            string entryRootPath = this.SessionState.Path.CurrentLocation.ToString();  // Root to convert absolute Filesystem Paths to relative Zip Entry paths
            string entryBaseDirectoryPath = null; // Base Sub Directory for entries  
            bool appendEntries = Append;


            // Set Paths from command parameters
            if ( ZipFile != null && ZipFile.Length > 0 )
                zipFilePath = Path.Combine(this.SessionState.Path.CurrentLocation.ToString(), ZipFile[0]);

            if (EntryFile != null && EntryFile.Length > 0)
                entryFilePath = Path.Combine(this.SessionState.Path.CurrentLocation.ToString(), EntryFile[0]);

            if (EntryZip != null && EntryZip.Length > 0)
                entryZipPath = EntryZip[0];

            if (EntryRoot != null && EntryRoot.Length > 0)
                entryRootPath = Path.Combine(this.SessionState.Path.CurrentLocation.ToString(), EntryRoot[0]);

            if (EntryDir != null && EntryDir.Length > 0)
                entryBaseDirectoryPath = EntryDir[0];


            if (PipelineObject != null)
            {
                string name = this.TryGetPipelinePropertyAsString(PipelineObject, "Name");
                string directoryName = this.TryGetPipelinePropertyAsString(PipelineObject, "DirectoryName");
                if (name != null)
                {
                    if (directoryName == null)
                        directoryName = "";

                    entryFilePath = Path.Combine(directoryName, name);
                }

                string zipFile = this.TryGetPipelinePropertyAsString(PipelineObject, "ZipFile");
                if (zipFile != null)
                {
                    zipFilePath = Path.Combine(this.SessionState.Path.CurrentLocation.ToString(), zipFile);
                }

                string entryRoot = this.TryGetPipelinePropertyAsString(PipelineObject, "EntryPathRoot");
                if (entryRoot != null)
                {
                    entryRootPath = Path.Combine(this.SessionState.Path.CurrentLocation.ToString(), entryRoot);
                }

            }

            if (entryZipPath == null || entryZipPath == String.Empty )
                entryZipPath = entryFilePath;

            if (zipFilePath != null)
            {

                SZL.ZipFile zipFile = null;
                if (!zipFiles.TryGetValue(zipFilePath.ToLower(), out zipFile))
                {
                    FileInfo zipFileInfo = new FileInfo(zipFilePath);

                    if (appendEntries && zipFileInfo.Exists && zipFileInfo.Length > 0)
                        zipFile = new SZL.ZipFile(zipFilePath);
                    else
                        zipFile = SZL.ZipFile.Create(zipFilePath);

                    zipFile.NameTransform = new SZL.ZipNameTransform();
                    zipFiles.Add(zipFilePath.ToLower(), zipFile);
                }
                if (zipFile != null)
                {
                    FileInfo fileInfo = new FileInfo(entryFilePath);
                    if (fileInfo.Exists && (fileInfo.Attributes & FileAttributes.Directory) == 0)
                    {
                        string entryName = this.GetSubPath(entryRootPath, entryZipPath);
                        if (entryName != null && entryName.Length > 0)
                        {
                            if (entryBaseDirectoryPath != null && entryBaseDirectoryPath.Length > 0)
                            {
                                if (!entryBaseDirectoryPath.EndsWith(new string(Path.DirectorySeparatorChar, 1)) && !entryBaseDirectoryPath.EndsWith(new string(Path.AltDirectorySeparatorChar, 1)))
                                    entryBaseDirectoryPath += Path.DirectorySeparatorChar;
                            }
                            else
                                entryBaseDirectoryPath = "";

                            entryName = entryBaseDirectoryPath + entryName;
                            if (entryName != null && entryName.Length > 0)
                            {
                                if(! zipFile.IsUpdating) 
                                    zipFile.BeginUpdate();

                                zipFile.Add(entryFilePath, entryName);
 }
                            else
                            {
                                // TODO: Implement Error Handling
                            }
                        }
                    }
                }
            }
            else
            {
                // TODO: Implement Error Handling
            }
            
        }

        protected override void EndProcessing()
        {
            foreach (KeyValuePair<string,SZL.ZipFile> zipFileEntry in zipFiles)
            {
                var zipFile = zipFileEntry.Value;
                if (zipFile.IsUpdating) 
                    zipFile.CommitUpdate();

                zipFile.Close();
                FileInfo fileInfo = new FileInfo(zipFileEntry.Key);
                this.WriteObject(fileInfo);
            }

            zipFiles = null;
        }

        protected override void StopProcessing()
        {
            foreach (KeyValuePair<string, SZL.ZipFile> zipFile in zipFiles)
            {
                zipFile.Value.Close();
            }
            zipFiles = null;
        }
    }
}
